/* m-reset.css v1.0.0 | MIT License | https://gitee.com/bugeiguojiatuohoutui/m-animation.js */

/**
 * tween 算法来自网络
 * t current time（当前时间）
 * b beginning value（初始值）
 * c change in value（变化量） = 目标位置 - 当前位置
 * d duration（持续时间）
 */
const Tween = {
  linear: function (t, b, c, d) {
    //匀速
    return (c * t) / d + b;
  },
  easeIn: function (t, b, c, d) {
    //加速曲线
    return c * (t /= d) * t + b;
  },
  easeOut: function (t, b, c, d) {
    //减速曲线
    return -c * (t /= d) * (t - 2) + b;
  },
  easeBoth: function (t, b, c, d) {
    //加速减速曲线
    if ((t /= d / 2) < 1) {
      return (c / 2) * t * t + b;
    }
    return (-c / 2) * (--t * (t - 2) - 1) + b;
  },
  easeInStrong: function (t, b, c, d) {
    //加加速曲线
    return c * (t /= d) * t * t * t + b;
  },
  easeOutStrong: function (t, b, c, d) {
    //减减速曲线
    return -c * ((t = t / d - 1) * t * t * t - 1) + b;
  },
  easeBothStrong: function (t, b, c, d) {
    //加加速减减速曲线
    if ((t /= d / 2) < 1) {
      return (c / 2) * t * t * t * t + b;
    }
    return (-c / 2) * ((t -= 2) * t * t * t - 2) + b;
  },
  elasticIn: function (t, b, c, d, a, p) {
    //正弦衰减曲线（弹动渐入）
    if (t === 0) {
      return b;
    }
    if ((t /= d) == 1) {
      return b + c;
    }
    if (!p) {
      p = d * 0.3;
    }
    if (!a || a < Math.abs(c)) {
      a = c;
      var s = p / 4;
    } else {
      var s = (p / (2 * Math.PI)) * Math.asin(c / a);
    }
    return (
      -(
        a *
        Math.pow(2, 10 * (t -= 1)) *
        Math.sin(((t * d - s) * (2 * Math.PI)) / p)
      ) + b
    );
  },
  elasticOut: function (t, b, c, d, a, p) {
    //*正弦增强曲线（弹动渐出）
    if (t === 0) {
      return b;
    }
    if ((t /= d) == 1) {
      return b + c;
    }
    if (!p) {
      p = d * 0.3;
    }
    if (!a || a < Math.abs(c)) {
      a = c;
      var s = p / 4;
    } else {
      var s = (p / (2 * Math.PI)) * Math.asin(c / a);
    }
    return (
      a * Math.pow(2, -10 * t) * Math.sin(((t * d - s) * (2 * Math.PI)) / p) +
      c +
      b
    );
  },
  elasticBoth: function (t, b, c, d, a, p) {
    if (t === 0) {
      return b;
    }
    if ((t /= d / 2) == 2) {
      return b + c;
    }
    if (!p) {
      p = d * (0.3 * 1.5);
    }
    if (!a || a < Math.abs(c)) {
      a = c;
      var s = p / 4;
    } else {
      var s = (p / (2 * Math.PI)) * Math.asin(c / a);
    }
    if (t < 1) {
      return (
        -0.5 *
          (a *
            Math.pow(2, 10 * (t -= 1)) *
            Math.sin(((t * d - s) * (2 * Math.PI)) / p)) +
        b
      );
    }
    return (
      a *
        Math.pow(2, -10 * (t -= 1)) *
        Math.sin(((t * d - s) * (2 * Math.PI)) / p) *
        0.5 +
      c +
      b
    );
  },
  backIn: function (t, b, c, d, s) {
    //回退加速（回退渐入）
    if (typeof s == "undefined") {
      s = 1.70158;
    }
    return c * (t /= d) * t * ((s + 1) * t - s) + b;
  },
  backOut: function (t, b, c, d, s) {
    if (typeof s == "undefined") {
      s = 1.70158; //回缩的距离
    }
    return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
  },
  backBoth: function (t, b, c, d, s) {
    if (typeof s == "undefined") {
      s = 1.70158;
    }
    if ((t /= d / 2) < 1) {
      return (c / 2) * (t * t * (((s *= 1.525) + 1) * t - s)) + b;
    }
    return (c / 2) * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b;
  },
  bounceIn: function (t, b, c, d) {
    //弹球减振（弹球渐出）
    return c - Tween["bounceOut"](d - t, 0, c, d) + b;
  },
  bounceOut: function (t, b, c, d) {
    //*
    if ((t /= d) < 1 / 2.75) {
      return c * (7.5625 * t * t) + b;
    } else if (t < 2 / 2.75) {
      return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b;
    } else if (t < 2.5 / 2.75) {
      return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b;
    }
    return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b;
  },
  bounceBoth: function (t, b, c, d) {
    if (t < d / 2) {
      return Tween["bounceIn"](t * 2, 0, c, d) * 0.5 + b;
    }
    return Tween["bounceOut"](t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
  },
};

// 用于 transform 相关属性判断
const transformAttrs = [
  // 'matrix', //(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
  // 'matrix3d', //(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0)
  // 'perspective', //(17px)
  "rotate", //(0.5turn)
  // 'rotate3d', //(1, 2.0, 3.0, 10deg)
  "rotateX", //(10deg)
  "rotateY", //(10deg)
  "rotateZ", //(10deg)
  "scale", //(2, 0.5)
  // 'scale3d', //(2.5, 1.2, 0.3)
  "scaleX", //(2)
  "scaleY", //(0.5)
  "scaleZ", //(0.3)
  "skew", //(30deg, 20deg)
  "skewX", //(30deg)
  "skewY", //(1.07rad)
  "translate", //(12px, 50%)
  // 'translate3d', //(12px, 50%, 3em)
  "translateX", //(2em)
  "translateY", //(3in)
  "translateZ", //(2px)
];

// 用于数值类型属性判断
const numericalAttrs = [
  "left",
  "right",
  "top",
  "bottom",
  "width",
  "height",
  "paddingTop",
  "paddingBottom",
  "paddingLeft",
  "paddingRight",
  "marginTop",
  "marginBottom",
  "marginLeft",
  "marginRight",
];

/**
 * 设置或者获取元素的transform属性值
 * @param el  要设置的元素
 * @param attr  变换属性： translate\scale\rotate....
 * @param value  变换属性的值 （可选） 如果指定了，设置；没有指定，获取
 */
function transformCSS(el, attr, value) {
  // 用 transformData 保存值，如果没有初始化，则需要先初始化
  el.transformData = el.transformData || {};

  // 这么判断避免出现值为 0 的情况
  if (value === undefined) {
    let result = el.transformData[attr];

    // 如果不加下面判断，每次获取值时需要先给默认值
    if (result === undefined) {
      if (["scale", "scaleX", "scaleY"].includes(attr)) {
        result = 1;
      } else {
        result = 0;
      }
    }
    return result;
  }

  let transformStr = "";
  el.transformData[attr] = value;

  for (let i in el.transformData) {
    switch (i) {
      case "translate":
      case "translateX":
      case "translateY":
      case "translateZ":
        transformStr += i + `(${el.transformData[i]}px) `;
        break;
      case "scale":
      case "scaleX":
      case "scaleY":
      case "scaleZ":
        transformStr += i + `(${el.transformData[i]}px) `;
        break;
      case "rotate":
      case "rotatX":
      case "rotatY":
      case "rotatZ":
      case "skew":
      case "skewX":
      case "skewY":
        transformStr += i + `(${el.transformData[i]}deg) `;
        break;
    }
    //设置css样式
    el.style.transform = transformStr.trim();
  }
}

/**
 * 设置或者获取元素的transform属性值
 * @param el  要设置的元素
 * @param attr 样式属性，可以是单值 width 这种；也可以是对象形式 {width:200,height:300}，如果是对象形式，则无需第三个参数
 * @param value  属性的值 （可选） 如果指定了，表示设置值
 */
function css(el, attr, value) {
  // transform 相关交给 transformCSS
  if (transformAttrs.includes(attr)) {
    return transformCSS(el, attr, value);
  }
  // 如果是 2 个参数，表示获取值
  if (value === undefined && typeof attr !== "object") {
    let result = getComputedStyle(el)[attr];
    if (numericalAttrs.includes(result) || !isNaN(parseFloat(result))) {
      result = parseFloat(result);
    }
    return result;
  } else {
    if (typeof attr === "object") {
      for (key in attr) {
        css(el, key, attr[key]);
      }
    } else if (attr === "opacity") {
      el.style.opacity = value;
    } else if (numericalAttrs.includes(attr)) {
      // 数值
      el.style[attr] = value + "px";
    } else if (attr === "zIndex") {
      // zIndex 不能是小数，否则无效
      el.style[attr] = parseInt(value);
    } else {
      // 复合属性
      el.style[attr] = value;
    }
  }
}

function mTween(opts) {
  let { el, attr, duration = 500, fx = "easeOut" } = opts;

  if (el.timer) return;

  // 新增
  let maxC = 0;
  if (typeof duration === "object") {
    let durationOpt = duration;
    duration.multiple = durationOpt.multiple || 2;
    duration = maxC * duration.multiple;

    duration = durationOpt.max ? Math.min(duration, durationOpt.max) : duration;
    duration = durationOpt.min ? Math.max(duration, durationOpt.min) : duration;
  }

  let t = 0;
  let b = {};
  let c = {};
  let d = Math.ceil(duration / (1000 / 60));

  for (key in attr) {
    b[key] = css(el, key);
    c[key] = attr[key] - b[key];
    maxC = Math.max(maxC, Math.abs(c[key]));
  }

  move();

  // 动画核心
  function move() {
    el.timer = requestAnimationFrame(() => {
      t++;
      if (t > d) {
        el.timer = null;
        opts.cb && opts.cb();
        // console.log('动画结束')
      } else {
        for (key in attr) {
          let value = Tween[fx](t, b[key], c[key], d);
          css(el, key, value);
        }
        move();
      }
    });
  }
}

mTween.stop = function (el) {
  cancelAnimationFrame(el.timer);
  el.timer = null;
};

// 抖动原理就是递减的正负值偏移

function shake(opts) {
  let { el, attr, shakeLength = 15 } = opts;
  let shakeArr = [];
  el.shakeStart = {};

  if (el.shake) {
    return;
  }

  if (typeof attr === "object") {
    for (let i = 0; i < attr.length; i++) {
      el.shakeStart[attr[i]] = css(el, attr[i]);
    }
  } else {
    el.shakeStart[attr] = css(el, attr);
  }
  for (let i = shakeLength; i >= 0; i--) {
    shakeArr.push(i % 2 ? i : -i);
  }

  function move() {
    el.shake = requestAnimationFrame(function () {
      if (shakeArr.length <= 0) {
        el.shake = false;
        opts.cb && opts.cb();
      } else {
        let num = shakeArr.shift();
        for (let s in el.shakeStart) {
          css(el, s, el.shakeStart[s] + num);
        }
        move();
      }
    });
  }
  move();
}

shake.stop = function (el) {
  cancelAnimationFrame(el.shake);
  el.shake = false;
  for (let s in el.shakeStart) {
    css(el, s, el.shakeStart[s]);
  }
};
